package org.infodavid.common.persistence;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import junit.framework.Assert;

import org.junit.Test;

import org.infodavid.common.dto.IUserListItem;
import org.infodavid.common.dto.IUserGroupListItem.EUserRole;
import org.infodavid.common.impl.model.criteria.UserCriteriaImpl;
import org.infodavid.common.model.IUser;
import org.infodavid.common.model.IUserGroup;
import org.infodavid.common.model.UserTest;
import org.infodavid.common.model.criteria.IUserCriteria;

/**
 * The Class UserDataServiceTest.
 */
@SuppressWarnings({
	"null"
})
public class UserDataServiceTest extends AbstractDataServiceTest<IUserListItem,IUser,Long> {

	/**
	 * The Constructor.
	 */
	public UserDataServiceTest() {
		super(new UserTest(), UserDataService.getInstance());
	}

	/*
	 * See super class or interface. (non-Javadoc)
	 * @see
	 * org.infodavid.common.persistence.AbstractDataServiceTest#modify(org.infodavid.common.model.IDataObject
	 * )
	 */
	@Override
	public void modify(final IUser item) throws Exception {
		item.setComment("ModifiedComment");
		item.setLastIP("ModifiedIP");

		if (item.getPerson() != null) {
			item.getPerson().setComment("ModifiedComment");
		}
	}

	/**
	 * Test get instance.
	 */
	@Test
	public final void testGetInstance() {
		Assert.assertEquals("Not a singleton", UserDataService.getInstance(),
		    UserDataService.getInstance());
	}

	/**
	 * Test find.
	 * @throws Exception the exception
	 */
	@Test
	public void testFindByCriteria() throws Exception {
		final List<IUserListItem> results = new ArrayList<IUserListItem>();
		final IUserCriteria criteria = new UserCriteriaImpl();
		final long count = ((UserDataService)getService()).find(getSession(), results, criteria);

		// System.out.println(results);

		Assert.assertNotNull("Result is null", results);
		Assert.assertEquals("Not all data has been found", ITEMS_TO_INSERT, results.size());
		Assert.assertEquals("Not all data has been found", ITEMS_TO_INSERT, count);
	}

	/**
	 * Test find.
	 * @throws Exception the exception
	 */
	@Test
	public void testFindByCriteriaUsingAllCriterions() throws Exception {
		final Collection<IUser> inserted = getTestData();
		final IUser user = inserted.iterator().next();
		final List<IUserListItem> results = new ArrayList<IUserListItem>();
		final IUserCriteria criteria = new UserCriteriaImpl();

		criteria.setNamePattern(user.getName());
		criteria.setGroupKey(user.getPrimaryGroup().getKey());

		final long count = ((UserDataService)getService()).find(getSession(), results, criteria);

		// System.out.println(results);

		Assert.assertNotNull("Result is null", results);
		Assert.assertEquals("Not all data has been found", 1, results.size());
		Assert.assertEquals("Not all data has been found", 1, count);
		Assert.assertEquals("Wrong data found", user, results.get(0));
	}

	/**
	 * Test find.
	 * @throws Exception the exception
	 */
	@Test
	public void testFindByCriteriaUsingNameNoDataFound() throws Exception {
		final List<IUserListItem> results = new ArrayList<IUserListItem>();
		final IUserCriteria criteria = new UserCriteriaImpl();

		criteria.setNamePattern("NoData");

		final long count = ((UserDataService)getService()).find(getSession(), results, criteria);

		// System.out.println(results);

		Assert.assertNotNull("Result is null", results);
		Assert.assertEquals("Not all data has been found", 0, results.size());
		Assert.assertEquals("Not all data has been found", 0, count);
	}

	/**
	 * Test find.
	 * @throws Exception the exception
	 */
	@Test
	public void testFindByCriteriaUsingNameEqual() throws Exception {
		final Collection<IUser> inserted = getTestData();
		final List<IUserListItem> results = new ArrayList<IUserListItem>();
		final IUserCriteria criteria = new UserCriteriaImpl();
		final IUser user = inserted.iterator().next();

		criteria.setNamePattern(user.getName());

		final long count = ((UserDataService)getService()).find(getSession(), results, criteria);

		// System.out.println(results);

		Assert.assertNotNull("Result is null", results);
		Assert.assertEquals("Not all data has been found", 1, results.size());
		Assert.assertEquals("Not all data has been found", 1, count);
		Assert.assertEquals("Wrong data found", user, results.get(0));
	}

	/**
	 * Test find.
	 * @throws Exception the exception
	 */
	@Test
	public void testFindByCriteriaUsingGroup() throws Exception {
		final Collection<IUser> inserted = getTestData();
		final List<IUserListItem> results = new ArrayList<IUserListItem>();
		final IUserCriteria criteria = new UserCriteriaImpl();
		final IUser user = inserted.iterator().next();

		criteria.setGroupKey(user.getPrimaryGroup().getKey());

		final long count = ((UserDataService)getService()).find(getSession(), results, criteria);

		// System.out.println(count);
		// System.out.println(results);

		Assert.assertNotNull("Result is null", results);
		Assert.assertEquals("Not all data has been found", 1, results.size());
		Assert.assertEquals("Not all data has been found", 1, count);
		Assert.assertEquals("Wrong data found", user, results.get(0));
	}

	/**
	 * Test find.
	 * @throws Exception the exception
	 */
	@Test
	public void testFindByCriteriaUsingNameLike() throws Exception {
		final List<IUserListItem> results = new ArrayList<IUserListItem>();
		final IUserCriteria criteria = new UserCriteriaImpl();

		criteria.setNamePattern("item%");

		final long count = ((UserDataService)getService()).find(getSession(), results, criteria);

		// System.out.println(count);
		// System.out.println(results);

		Assert.assertNotNull("Result is null", results);
		Assert.assertEquals("Not all data has been found", ITEMS_TO_INSERT, results.size());
		Assert.assertEquals("Not all data has been found", ITEMS_TO_INSERT, count);
	}

	/**
	 * Test find.
	 * @throws Exception the exception
	 */
	@Test
	public void testFindByCriteriaUsingNullCollection() throws Exception {
		final IUserCriteria criteria = new UserCriteriaImpl();
		Exception exception = null;

		try {
			((UserDataService)getService()).find(getSession(), null, criteria);
		}
		catch (final Exception e) {
			exception = e;
			e.printStackTrace();
		}

		Assert.assertNotNull("No exception thrown", exception);
		Assert.assertEquals("Wrong exception thrown", IllegalArgumentException.class,
		    exception.getClass());
	}

	/**
	 * Test find.
	 * @throws Exception the exception
	 */
	@Test
	public void testFindByCriteriaUsingNullCriteria() throws Exception {
		final List<IUserListItem> results = new ArrayList<IUserListItem>();
		final long count =
		    ((UserDataService)getService()).find(getSession(), results, (IUserCriteria)null);

		// System.out.println(results);

		Assert.assertNotNull("Result is null", results);
		Assert.assertEquals("Not all data has been found", ITEMS_TO_INSERT, results.size());
		Assert.assertEquals("Not all data has been found", ITEMS_TO_INSERT, count);
	}

	/**
	 * Test find.
	 * @throws Exception the exception
	 */
	@Test
	public void testFindByCriteriaUsingPage() throws Exception {
		final List<IUserListItem> results = new ArrayList<IUserListItem>();
		final IUserCriteria criteria = new UserCriteriaImpl();

		criteria.setPageStart(Long.valueOf(0));
		criteria.setPageSize(Integer.valueOf(5));

		final long count = ((UserDataService)getService()).find(getSession(), results, criteria);

		// System.out.println(results);

		Assert.assertNotNull("Result is null", results);
		Assert.assertEquals("Not all data has been found", criteria.getPageSize().intValue(),
		    results.size());
		Assert.assertEquals("Not all data has been found", ITEMS_TO_INSERT, count);
	}

	/**
	 * Test find.
	 * @throws Exception the exception
	 */
	@Test
	public void testFindByName() throws Exception {
		final Collection<IUser> inserted = getTestData();
		final IUser result =
		    ((UserDataService)getService()).findByName(getSession(), inserted.iterator().next()
		        .getName());

		Assert.assertNotNull("Result is null", result);
		Assert.assertEquals("Wrong data found", inserted.iterator().next().getName(), result.getName());
		Assert.assertEquals("Wrong data found", inserted.iterator().next().getKey(), result.getKey());
	}

	/**
	 * Test find.
	 * @throws Exception the exception
	 */
	@Test
	public void testFindByNameUsingNull() throws Exception {
		Exception exception = null;

		try {
			((UserDataService)getService()).findByName(getSession(), null);
		}
		catch (final Exception e) {
			exception = e;
			e.printStackTrace();
		}

		Assert.assertNull("Exception thrown", exception);
	}

	/**
	 * Test find.
	 * @param role the role
	 * @throws Exception the exception
	 */
	private void assertFindByRole(final EUserRole role) throws Exception {
		final Collection<IUser> inserted = getTestData();
		final List<IUser> results = new ArrayList<IUser>();
		getSession().getTransaction().commit();
		final long count =
		    ((UserDataService)getService()).findByRole(getSession(), role, results, null);

		// System.out.println(results);

		int expectedCount = 0;
		boolean found;

		for (final IUser item : inserted) {
			// System.out.println(item.getName());
			found = false;

			for (final IUserGroup grp : item.getGroups().values()) {
				// System.out.println(grp.getRole());
				if (grp.getRole() == role) {
					found = true;
					break;
				}
			}

			expectedCount += found ? 1 : 0;
		}

		Assert.assertNotNull("Result is null", results);
		Assert.assertEquals("Not all data has been found", expectedCount, results.size());
		Assert.assertEquals("Not all data has been found", expectedCount, count);

		for (final IUserListItem item : results) {
			found = false;
			final IUser user = getService().findByPrimaryKey(getSession(), item.getKey());

			if (user.getPrimaryGroup().getRole() == role) {
				found = true;
			}
			else {
				for (final IUserGroup grp : user.getGroups().values()) {
					if (grp.getRole() == role) {
						found = true;
						break;
					}
				}
			}

			Assert.assertTrue("Wrong result", found);
		}
	}

	/**
	 * Test find.
	 * @throws Exception the exception
	 */
	@Test
	public void testFindByAdministratorRole() throws Exception {
		assertFindByRole(EUserRole.ADMINISTRATORS);
	}

	/**
	 * Test find.
	 * @throws Exception the exception
	 */
	@Test
	public void testFindByNormalRole() throws Exception {
		assertFindByRole(EUserRole.NORMAL);
	}

	/**
	 * Test find.
	 * @throws Exception the exception
	 */
	@Test
	public void testFindBySystemRole() throws Exception {
		assertFindByRole(EUserRole.SYSTEM);
	}

	/**
	 * Test find.
	 * @throws Exception the exception
	 */
	@Test
	public void testFindByRoleUsingNull() throws Exception {
		Exception exception = null;
		final List<IUser> results = new ArrayList<IUser>();

		try {
			((UserDataService)getService()).findByRole(getSession(), null, results, null);
		}
		catch (final Exception e) {
			exception = e;
			e.printStackTrace();
		}

		Assert.assertNotNull("No exception thrown", exception);
		Assert.assertEquals("Wrong exception thrown", IllegalArgumentException.class,
		    exception.getClass());
	}

	/**
	 * Test find.
	 * @throws Exception the exception
	 */
	@Test
	public void testFindByRoleUsingNullCollection() throws Exception {
		Exception exception = null;

		try {
			((UserDataService)getService()).findByRole(getSession(), null, null, null);
		}
		catch (final Exception e) {
			exception = e;
			e.printStackTrace();
		}

		Assert.assertNotNull("No exception thrown", exception);
		Assert.assertEquals("Wrong exception thrown", IllegalArgumentException.class,
		    exception.getClass());
	}

	/**
	 * Test find.
	 * @throws Exception the exception
	 */
	@Test
	public void testFindByGroup() throws Exception {
		final Collection<IUser> inserted = getTestData();
		final List<IUserListItem> results = new ArrayList<IUserListItem>();
		final List<IUserGroup> groups =
		    new ArrayList<IUserGroup>(inserted.iterator().next().getGroups().values());
		final Long group = groups.get(groups.size() - 1).getKey();
		final long count =
		    ((UserDataService)getService()).findByGroup(getSession(), group, results, null);

		// System.out.println(results);

		int expectedCount = 0;
		boolean found;

		for (final IUser item : inserted) {
			found = false;

			if (item.getPrimaryGroup().getKey() == group) {
				found = true;
			}
			else {
				for (final IUserGroup grp : item.getGroups().values()) {
					if (grp.getKey() == group) {
						found = true;
						break;
					}
				}
			}

			expectedCount += found ? 1 : 0;
		}

		Assert.assertNotNull("Result is null", results);
		Assert.assertEquals("Not all data has been found", expectedCount, results.size());
		Assert.assertEquals("Not all data has been found", expectedCount, count);

		for (final IUserListItem item : results) {
			found = false;
			final IUser user = getService().findByPrimaryKey(getSession(), item.getKey());

			if (user.getPrimaryGroup().getKey() == group) {
				found = true;
			}
			else {
				for (final IUserGroup grp : user.getGroups().values()) {
					if (grp.getKey() == group) {
						found = true;
						break;
					}
				}
			}

			Assert.assertTrue("Wrong result", found);
		}
	}

	/**
	 * Test find.
	 * @throws Exception the exception
	 */
	@Test
	public void testFindByGroupUsingNull() throws Exception {
		Exception exception = null;
		final List<IUserListItem> results = new ArrayList<IUserListItem>();

		try {
			((UserDataService)getService()).findByGroup(getSession(), null, results, null);
		}
		catch (final Exception e) {
			exception = e;
			e.printStackTrace();
		}

		Assert.assertNotNull("No exception thrown", exception);
		Assert.assertEquals("Wrong exception thrown", IllegalArgumentException.class,
		    exception.getClass());
	}

	/**
	 * Test find.
	 * @throws Exception the exception
	 */
	@Test
	public void testFindByGroupUsingNullCollection() throws Exception {
		Exception exception = null;

		try {
			((UserDataService)getService()).findByGroup(getSession(), null, null, null);
		}
		catch (final Exception e) {
			exception = e;
			e.printStackTrace();
		}

		Assert.assertNotNull("No exception thrown", exception);
		Assert.assertEquals("Wrong exception thrown", IllegalArgumentException.class,
		    exception.getClass());
	}

	/**
	 * Test find.
	 * @throws Exception the exception
	 */
	@Test
	public void testFindByEmail() throws Exception {
		final Collection<IUser> inserted = getTestData();
		final IUser result =
		    ((UserDataService)getService()).findByEmail(getSession(), inserted.iterator().next()
		        .getEmail().getValue());

		Assert.assertNotNull("Result is null", result);
		Assert.assertEquals("Wrong data found", inserted.iterator().next().getName(), result.getName());
		Assert.assertEquals("Wrong data found", inserted.iterator().next().getKey(), result.getKey());
	}

	/**
	 * Test find.
	 * @throws Exception the exception
	 */
	@Test
	public void testFindByEmailUsingNull() throws Exception {
		Exception exception = null;

		try {
			((UserDataService)getService()).findByEmail(getSession(), null);
		}
		catch (final Exception e) {
			exception = e;
			e.printStackTrace();
		}

		Assert.assertNotNull("No exception thrown", exception);
		Assert.assertEquals("Wrong exception thrown", IllegalArgumentException.class,
		    exception.getClass());
	}

	/**
	 * Test find.
	 * @throws Exception the exception
	 */
	@Test
	public void testFindRoles() throws Exception {
		final Collection<IUser> inserted = getTestData();
		final IUser user = inserted.iterator().next();
		final Collection<EUserRole> results =
		    ((UserDataService)getService()).findRoles(getSession(), user.getKey());
		final Set<String> roles = new HashSet<String>();

		// System.out.println(results);

		for (final IUserGroup grp : user.getGroups().values()) {
			roles.add(grp.getRole().name());
		}

		Assert.assertNotNull("Result is null", results);
		Assert.assertEquals("Not all data has been found", roles.size(), results.size());
	}

	/**
	 * Test find.
	 * @throws Exception the exception
	 */
	@Test
	public void testFindRolesUsingNull() throws Exception {
		Exception exception = null;

		try {
			((UserDataService)getService()).findRoles(getSession(), null);
		}
		catch (final Exception e) {
			exception = e;
			e.printStackTrace();
		}

		Assert.assertNotNull("No exception thrown", exception);
		Assert.assertEquals("Wrong exception thrown", IllegalArgumentException.class,
		    exception.getClass());
	}
}
